package t10086.Data;

import t10086.Controller.FailedController;
import t10086.Data.Map.GridType;
import t10086.Data.Map.MapGrid;
import t10086.Data.Map.T10086Map;
import t10086.Data.Map.TextCoordinate;
import t10086.Data.Pop.COLLISION;
import t10086.Data.Pop.PopMsg;
import t10086.Data.Pop.PopType;
import t10086.Data.Robot.RobotInfo;
import t10086.Data.Robot.RobotQueue;
import t10086.Data.Robot.RobotStatus;
import t10086.Data.Robot.RobotType;
import t10086.WorkThread.Shared.ClientMap;
import t10086.WorkThread.Shared.DataBuffer;

import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


public class Broadcast implements Serializable {
    //cyc
    private T10086Map map;
    private Map<String, RobotInfo> robotInfoMap;//使用序列号标记
    private Long movingNumber;//移动序列号
    private Long queuingNumber;//排队序列号
    private RobotQueue movingQueue;//移动队列
    private Map<Integer,Map<String,RobotInfo>> queuingQueues;//排队队列
    private Map<String,RobotInfo> freeQueue;//空闲队列
    private Map<String,RobotInfo> errorQueue;//异常队列
    private Map<Integer, TextCoordinate> windowMap;
    private TextCoordinate deliverCoordinate;
    private List<PopMsg>popQueue;

    //广播则广播该类
    public  Broadcast() {
            map = new T10086Map();
            robotInfoMap = new HashMap<String, RobotInfo>();
            movingNumber = 0L;
            queuingNumber = 0L;
            movingQueue = new RobotQueue();
            queuingQueues = new HashMap<>();
            freeQueue = new HashMap<>();
            errorQueue = new HashMap<>();
            windowMap = new HashMap<>();
            deliverCoordinate = new TextCoordinate(0,0);
            popQueue = new LinkedList<>();
        }

    public  Broadcast(Broadcast broadcast) {
            map = broadcast.getMap();
            robotInfoMap =new HashMap<>(broadcast.getRobotInfoMap());
            movingNumber = broadcast.getMovingNumber();
            queuingNumber = broadcast.getQueuingNumber();
            movingQueue = new RobotQueue(broadcast.getMovingQueue());
            queuingQueues =new HashMap<>(broadcast.getQueuingQueues());
            freeQueue = new HashMap<>(broadcast.getFreeQueue());
            errorQueue = new HashMap<>(broadcast.getErrorQueue());
            windowMap = new HashMap<>(broadcast.getWindowMap());
            deliverCoordinate = broadcast.getDeliverCoordinate();
            popQueue =new LinkedList<>(broadcast.getPopQueue());
    }
    public synchronized void addPopMsg(PopMsg popMsg) {
        popQueue.add(popMsg);
    }
    public synchronized void clearPopQueue() {
        popQueue.clear();
    }
    public synchronized void clear(){
            map = new T10086Map();
            robotInfoMap.clear();
            movingNumber = 0L;
            queuingNumber = 0L;
            movingQueue.clear();
            queuingQueues.clear();
            freeQueue.clear();
            errorQueue.clear();
            windowMap.clear();
            deliverCoordinate = new TextCoordinate(0,0);
            popQueue.clear();
    }

    public synchronized LinkedList<RobotInfo> getFreeRobotInfos(){
        return new LinkedList<>(freeQueue.values());
    }

    public void autoUpdate(){
        boolean flag = true;
        for(RobotInfo robotInfo : movingQueue.getRobots().values()){
                   if(robotInfo.getSelfMovingNumber()<=movingNumber){
                       flag = false;
                       break;
                   }
        }
        if(flag){
            movingNumber++;
        }
    }
    public synchronized void removeClient(String serialNumber){
        if (robotInfoMap.containsKey(serialNumber)) {
            RobotInfo info = robotInfoMap.get(serialNumber);
            robotInfoMap.remove(serialNumber);
            ClientMap.removeClientBySerialNumber(serialNumber);
            if (info.getRobotStatus()==RobotStatus.QUEUING){
                for(Map<String,RobotInfo> robotQueue : queuingQueues.values()){
                    robotQueue.remove(serialNumber);
                }
            }
            else if (info.getRobotStatus()==RobotStatus.MOVING){
                movingQueue.remove(serialNumber,movingNumber);
            }
            else if (info.getRobotStatus()==RobotStatus.FREE){
                freeQueue.remove(serialNumber);
            }else{
                return;
            }
        }
    }
    public synchronized void putRobotInfo(String serialNumber,RobotInfo info) {
        //for deliver register
        robotInfoMap.put(serialNumber, info);
    }
    public synchronized boolean putAndAddInfo(String serialNumber,RobotInfo info) {
        //for robot register

        robotInfoMap.put(serialNumber, info);
        TextCoordinate textCoordinate =info.getPosition();
        MapGrid grid = map.getGrid(textCoordinate);
        if(grid.getGridType()!=GridType.NULL){
            System.out.printf("Robot [%s] REGISTER COLLISION!",info.getSerialNumber());
            return false;
        }
        grid.setRobotSerialNumber(serialNumber);
        grid.setGridType(GridType.ROBOT);
        map.getGrid(info.getPosition()).plusOne();
        freeQueue.put(serialNumber, info);
        return true;
    }
    public synchronized void updateRobotInfo(RobotInfo newRobotInfo){
        //机器人上传状态，更新地图信息

        RobotInfo oldRobotInfo = robotInfoMap.get(newRobotInfo.getSerialNumber());
        if(oldRobotInfo==null){
//            System.out.println("1");
            ClientMap.removeClientBySerialNumber(newRobotInfo.getSerialNumber());
            System.out.println("NULL SERIAL NUMBER");
            return;
        }
//        System.out.println("Robot["+newRobotInfo.getSerialNumber()+"] Move FROM "+
//                oldRobotInfo.getPosition()+" TO "+newRobotInfo.getPosition()+"Moving ["+newRobotInfo.getSelfMovingNumber()+"]"+
//                "GMOVING ["+movingNumber+"]");
        MapGrid oldGrid =  map.getGrid(oldRobotInfo.getPosition());
        if(oldGrid.getGridType()!=GridType.ROBOT){//撞上
            oldGrid.setGridType(GridType.COLLISION);
            removeClient(newRobotInfo.getSerialNumber());
            errorQueue.put(newRobotInfo.getSerialNumber(),newRobotInfo);
            DataBuffer.getBroadcast().addPopMsg(new PopMsg(
                    PopType.COLLISION,
                    RobotType.ROBOT,
                    oldGrid.getRobotSerialNumber(),
                    "Make COLLISION WITH ROBOT"+newRobotInfo.getSerialNumber()
            ));
            FailedController.putPopMsg(new COLLISION(oldGrid.getRobotSerialNumber(),newRobotInfo.getSerialNumber(),oldRobotInfo.getPosition()));
            System.out.println("Robot["+newRobotInfo.getSerialNumber()+"]"+"COLLISION");
            return;
        }
        oldGrid.setGridType(GridType.NULL);
        oldGrid.minusOne();//去除旧的
        MapGrid newGrid = map.getGrid(newRobotInfo.getPosition());
        if(newGrid.getGridType()!=GridType.NULL){//发生碰撞
            newGrid.setGridType(GridType.COLLISION);
            removeClient(newRobotInfo.getSerialNumber());
            errorQueue.put(newRobotInfo.getSerialNumber(),newRobotInfo);
            DataBuffer.getBroadcast().addPopMsg(new PopMsg(
                    PopType.COLLISION,
                    RobotType.ROBOT,
                    newRobotInfo.getSerialNumber(),
                    "Make COLLISION WITH ROBOT "+oldRobotInfo.getSerialNumber()
            ));
            FailedController.putPopMsg(new COLLISION(newRobotInfo.getSerialNumber(),oldRobotInfo.getSerialNumber(),newRobotInfo.getPosition()));
            System.out.println("Robot["+newRobotInfo.getSerialNumber()+"]"+"COLLISION");
            return;
        }
        newGrid.setGridType(GridType.ROBOT);
        newGrid.setRobotSerialNumber(oldRobotInfo.getSerialNumber());
        newGrid.plusOne();//更新新的
//        System.out.printf("%d,%d\n",movingNumber,newRobotInfo.getSelfMovingNumber());
        //序列号更新
        movingQueue.updateNumber(oldRobotInfo.getSelfMovingNumber(),
                newRobotInfo.getSelfMovingNumber(),movingNumber);
        oldRobotInfo.setAll(newRobotInfo);
    }
    public synchronized long FreeToMoving(String serialNumber) {
        if (robotInfoMap.containsKey(serialNumber)) {
            RobotInfo info = robotInfoMap.get(serialNumber);
            info.setSelfMovingNumber(0);
            if(info.getRobotStatus() == RobotStatus.FREE){
                info.setRobotStatus(RobotStatus.MOVING);
                freeQueue.remove(serialNumber);
                movingQueue.add(serialNumber, info,movingNumber);
                System.out.printf("FreeToMoving:Robot [%s]\n",info.getSerialNumber());
                movingQueue.plusOne();
                return movingNumber+1;
            }
        }
        System.out.println("FreeToMoving Error serialNumber:"+serialNumber);
        return -1;
    }
    public synchronized void QueuingToMoving(String serialNumber,Integer windowNumber) {
        if (robotInfoMap.containsKey(serialNumber)) {
            RobotInfo info = robotInfoMap.get(serialNumber);
            info.setSelfMovingNumber(0);
            if(info.getRobotStatus() == RobotStatus.QUEUING){
                info.setRobotStatus(RobotStatus.MOVING);
//                System.out.println("Sb:"+windowNumber);
                queuingQueues.get(windowNumber).remove(serialNumber);
                System.out.printf("QueuingToMoving:Robot [%s]  Window [%d]\n",info.getSerialNumber(),windowNumber);
            }
        }
        System.out.println("QueuingToMoving Error serialNumber:"+serialNumber);
    }

    public synchronized void MovingToQueuing(String serialNumber,Integer windowNumber) {
        if (robotInfoMap.containsKey(serialNumber)) {
            RobotInfo info = robotInfoMap.get(serialNumber);
            info.setSelfQueuingNumber(0);
            if(info.getRobotStatus() == RobotStatus.MOVING){
                info.setRobotStatus(RobotStatus.QUEUING);
                queuingQueues.get(windowNumber).put(serialNumber, info);
                System.out.printf("MovingToQueuing:Robot [%s]  Window [%d]\n",info.getSerialNumber(),windowNumber);
                return;
            }
        }
        System.out.println("MovingToQueuing Error serialNumber:"+serialNumber);
    }

    public synchronized void MovingToFree(String serialNumber) {
        if (robotInfoMap.containsKey(serialNumber)) {
            RobotInfo info = robotInfoMap.get(serialNumber);
            if(info.getRobotStatus() == RobotStatus.MOVING){
                info.setRobotStatus(RobotStatus.FREE);
                movingQueue.remove(serialNumber,movingNumber);
                freeQueue.put(serialNumber, info);
                System.out.printf("MovingToFree:Robot [%s]\n",info.getSerialNumber());
                return;
            }
        }
        System.out.println("MovingToFree Error serialNumber:"+serialNumber);
    }
    public synchronized void addWindow(Integer windowNumber,TextCoordinate textCoordinate) {
        windowMap.put(windowNumber, textCoordinate);
    }
    public synchronized void addWindowQueue(Integer windowNumber,Map<String,RobotInfo> robotQueue) {
        queuingQueues.put(windowNumber, robotQueue);
    }
    public synchronized T10086Map getMap() {
        return map;
    }

    public synchronized void setMap(T10086Map map) {
        this.map = map;
    }

    public synchronized Map<String, RobotInfo> getRobotInfoMap() {
        return robotInfoMap;
    }

    public synchronized void setRobotInfoMap(Map<String, RobotInfo> robotInfoMap) {
        this.robotInfoMap = robotInfoMap;
    }

    public synchronized Long getMovingNumber() {
        return movingNumber;
    }

    public synchronized void setMovingNumber(Long movingNumber) {
        this.movingNumber = movingNumber;
    }

    public synchronized Long getQueuingNumber() {
        return queuingNumber;
    }

    public synchronized void setQueuingNumber(Long queuingNumber) {
        this.queuingNumber = queuingNumber;
    }

    public synchronized RobotQueue getMovingQueue() {
        return movingQueue;
    }

    public synchronized void setMovingQueue(RobotQueue movingQueue) {
        this.movingQueue = movingQueue;
    }


    public synchronized Map<String, RobotInfo> getFreeQueue() {
        return freeQueue;
    }

    public synchronized void setFreeQueue(Map<String, RobotInfo> freeQueue) {
        this.freeQueue = freeQueue;
    }

    public synchronized Map<String, RobotInfo> getErrorQueue() {
        return errorQueue;
    }

    public synchronized void setErrorQueue(Map<String, RobotInfo> errorQueue) {
        this.errorQueue = errorQueue;
    }

    public synchronized Map<Integer, TextCoordinate> getWindowMap() {
        return windowMap;
    }

    public synchronized void setWindowMap(Map<Integer, TextCoordinate> windowMap) {
        this.windowMap = windowMap;
    }

    public synchronized Map<Integer, Map<String,RobotInfo>> getQueuingQueues() {
        return queuingQueues;
    }

    public synchronized void setQueuingQueues( Map<Integer, Map<String,RobotInfo>> queuingQueues) {
        this.queuingQueues = queuingQueues;
    }

    public synchronized TextCoordinate getDeliverCoordinate() {
        return deliverCoordinate;
    }

    public synchronized void setDeliverCoordinate(TextCoordinate deliverCoordinate) {
        this.deliverCoordinate = deliverCoordinate;
    }

    public List<PopMsg> getPopQueue() {
        return popQueue;
    }

    public void setPopQueue(List<PopMsg> popQueue) {
        this.popQueue = popQueue;
    }
}
